/*
 * This file is part of JSTUN.
 *
 * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
 * reserved.
 *
 * This software is licensed under either the GNU Public License (GPL),
 * or the Apache 2.0 license. Copies of both license agreements are
 * included in this distribution.
 */
package de.javawi.jstun.attribute;

import de.javawi.jstun.util.Utility;
import de.javawi.jstun.util.UtilityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class MessageAttribute implements MessageAttributeInterface {
  private static Logger LOGGER = LoggerFactory.getLogger(MessageAttribute.class);
  MessageAttributeType type;

  public MessageAttribute() {}

  public MessageAttribute(MessageAttributeType type) {
    setType(type);
  }

  public void setType(MessageAttributeType type) {
    this.type = type;
  }

  public MessageAttribute.MessageAttributeType getType() {
    return type;
  }

  public static int typeToInteger(MessageAttributeType type) {
    if (type == MessageAttributeType.MappedAddress) {
      return MAPPEDADDRESS;
    }
    if (type == MessageAttributeType.ResponseAddress) {
      return RESPONSEADDRESS;
    }
    if (type == MessageAttributeType.ChangeRequest) {
      return CHANGEREQUEST;
    }
    if (type == MessageAttributeType.SourceAddress) {
      return SOURCEADDRESS;
    }
    if (type == MessageAttributeType.ChangedAddress) {
      return CHANGEDADDRESS;
    }
    if (type == MessageAttributeType.Username) {
      return USERNAME;
    }
    if (type == MessageAttributeType.Password) {
      return PASSWORD;
    }
    if (type == MessageAttributeType.MessageIntegrity) {
      return MESSAGEINTEGRITY;
    }
    if (type == MessageAttributeType.ErrorCode) {
      return ERRORCODE;
    }
    if (type == MessageAttributeType.UnknownAttribute) {
      return UNKNOWNATTRIBUTE;
    }
    if (type == MessageAttributeType.ReflectedFrom) {
      return REFLECTEDFROM;
    }
    if (type == MessageAttributeType.ConnectionRequestBinding) {
      return CONNECTIONREQUESTBINDING;
    }
    if (type == MessageAttributeType.BindingChange) {
      return BINDINGCHANGE;
    }
    if (type == MessageAttributeType.Dummy) {
      return DUMMY;
    }
    return -1;
  }

  public static MessageAttributeType intToType(long type) {
    if (type == MAPPEDADDRESS) {
      return MessageAttributeType.MappedAddress;
    }
    if (type == RESPONSEADDRESS) {
      return MessageAttributeType.ResponseAddress;
    }
    if (type == CHANGEREQUEST) {
      return MessageAttributeType.ChangeRequest;
    }
    if (type == SOURCEADDRESS) {
      return MessageAttributeType.SourceAddress;
    }
    if (type == CHANGEDADDRESS) {
      return MessageAttributeType.ChangedAddress;
    }
    if (type == USERNAME) {
      return MessageAttributeType.Username;
    }
    if (type == PASSWORD) {
      return MessageAttributeType.Password;
    }
    if (type == MESSAGEINTEGRITY) {
      return MessageAttributeType.MessageIntegrity;
    }
    if (type == ERRORCODE) {
      return MessageAttributeType.ErrorCode;
    }
    if (type == UNKNOWNATTRIBUTE) {
      return MessageAttributeType.UnknownAttribute;
    }
    if (type == REFLECTEDFROM) {
      return MessageAttributeType.ReflectedFrom;
    }
    if (type == CONNECTIONREQUESTBINDING) {
      return MessageAttributeType.ConnectionRequestBinding;
    }
    if (type == BINDINGCHANGE) {
      return MessageAttributeType.BindingChange;
    }
    if (type == DUMMY) {
      return MessageAttributeType.Dummy;
    }
    return null;
  }

  public abstract byte[] getBytes() throws UtilityException;

  /**
   * Abstract public MessageAttribute parse(byte[] data) throws MessageAttributeParsingException;.
   */
  public int getLength() throws UtilityException {
    return getBytes().length;
  }

  public static MessageAttribute parseCommonHeader(byte[] data)
      throws MessageAttributeParsingException {
    try {
      byte[] typeArray = new byte[2];
      System.arraycopy(data, 0, typeArray, 0, 2);
      int type = Utility.twoBytesToInteger(typeArray);
      byte[] lengthArray = new byte[2];
      System.arraycopy(data, 2, lengthArray, 0, 2);
      int lengthValue = Utility.twoBytesToInteger(lengthArray);
      byte[] valueArray = new byte[lengthValue];
      System.arraycopy(data, 4, valueArray, 0, lengthValue);
      MessageAttribute ma;
      switch (type) {
        case MAPPEDADDRESS:
          ma = MappedAddress.parse(valueArray);
          break;
        case RESPONSEADDRESS:
          ma = ResponseAddress.parse(valueArray);
          break;
        case CHANGEREQUEST:
          ma = ChangeRequest.parse(valueArray);
          break;
        case SOURCEADDRESS:
          ma = SourceAddress.parse(valueArray);
          break;
        case CHANGEDADDRESS:
          ma = ChangedAddress.parse(valueArray);
          break;
        case USERNAME:
          ma = Username.parse(valueArray);
          break;
        case PASSWORD:
          ma = Password.parse(valueArray);
          break;
        case MESSAGEINTEGRITY:
          ma = MessageIntegrity.parse(valueArray);
          break;
        case ERRORCODE:
          ma = ErrorCode.parse(valueArray);
          break;
        case UNKNOWNATTRIBUTE:
          ma = UnknownAttribute.parse(valueArray);
          break;
        case REFLECTEDFROM:
          ma = ReflectedFrom.parse(valueArray);
          break;
        case CONNECTIONREQUESTBINDING:
          ma = ConnectionRequestBinding.parse(valueArray);
          break;
        case BINDINGCHANGE:
          ma = BindingChange.parse(valueArray);
          break;
        default:
          if (type <= 0x7fff) {
            throw new UnknownMessageAttributeException(
                "Unkown mandatory message attribute", intToType(type));
          } else {
            LOGGER.debug("MessageAttribute with type " + type + " unkown.");
            ma = Dummy.parse(valueArray);
            break;
          }
      }
      return ma;
    } catch (UtilityException ue) {
      throw new MessageAttributeParsingException("Parsing error");
    }
  }
}
